using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System.Collections.ObjectModel;

namespace Oops.Xna.Framework.Graphics
{
	/// <summary>
	/// Defines skinning data for an animated <see cref="Model"/>.
	/// </summary>
	public class SkinningData
	{
		#region AnimationClipCollection Declaration

		/// <summary>
		/// Represents a collection of <see cref="AnimationClip"/> instances.
		/// </summary>
		public class AnimationClipCollection : KeyedCollection<string, AnimationClip>
		{
			/// <summary>
			/// Initializes a new instance of the <see cref="AnimationClipCollection"/> class.
			/// </summary>
			internal AnimationClipCollection() { }

			#region KeyedCollection<string, AnimationClip> Members

			protected override string GetKeyForItem(AnimationClip item)
			{
				return item.Name;
			}

			#endregion
		}

		#endregion

		private AnimationClipCollection animationClips = new AnimationClipCollection();

		internal SkinningData(
			IList<Matrix> bindPose,
			IList<Matrix> inverseBindPose,
			IList<int> skeletonHierarchy,
			Texture2D animationTexture,
			IList<AnimationClip> animationClips,
			IDictionary<string, int> boneIndices)
		{
			this.BindPose = bindPose;
			this.InverseBindPose = inverseBindPose;
			this.SkeletonHierarchy = skeletonHierarchy;
			this.AnimationTexture = animationTexture;
			foreach (var clip in animationClips)
			{
				this.animationClips.Add(clip);
			}
			this.BoneIndices = boneIndices;
		}

		/// <summary>
		/// Gets the bind pose of the <see cref="SkinningData"/>.
		/// </summary>
		public IList<Matrix> BindPose { get; private set; }
		
		/// <summary>
		/// Gets the inverse bind pose of the <see cref="SkinningData"/>.
		/// </summary>
		public IList<Matrix> InverseBindPose { get; private set; }
		
		/// <summary>
		/// Gets the skeleton hierarchy of the <see cref="SkinningData"/>.
		/// </summary>
		public IList<int> SkeletonHierarchy { get; private set; }
		
		/// <summary>
		/// Gets the texture that stores the animation keyframes of the <see cref="SkinningData"/>.
		/// </summary>
		public Texture2D AnimationTexture { get; private set; }
		
		/// <summary>
		/// Gets the collection of <see cref="AnimationClip"/> instances of 
		/// the <see cref="SkinningData"/>.
		/// </summary>
		public AnimationClipCollection AnimationClips
		{
			get { return this.animationClips; }
		}

		public IDictionary<string, int> BoneIndices { get; private set; }

		internal static SkinningData Read(ContentReader input)
		{
			return new SkinningData(
				input.ReadObject<IList<Matrix>>(),
				input.ReadObject<IList<Matrix>>(),
				input.ReadObject<IList<int>>(),
				input.ReadObject<Texture2D>(),
				input.ReadObject<IList<AnimationClip>>(),
				input.ReadObject<IDictionary<string, int>>());
		}
	}

	/// <summary>
	/// Worker for reading a <see cref="SkinningData"/> instance from a binary format.
	/// </summary>
	public class SkinningDataReader : ContentTypeReader<SkinningData>
	{
		#region ContentTypeReader<SkinningData> Members

		protected override SkinningData Read(ContentReader input, SkinningData existingInstance)
		{
			return SkinningData.Read(input);
		}

		#endregion
	}
}
